﻿using System.Reflection;

namespace SageKing.Core.Contracts.FilterTypes;

public class EnumFieldFilterType<T> : BaseFieldFilterType
{
    private static readonly MethodInfo _enumHasFlag = typeof(Enum).GetMethod(nameof(Enum.HasFlag));
    
    private static IEnumerable<SageKingFilterCompareOperator> _supportedCompareOperators = new[]
    {
        SageKingFilterCompareOperator.Equals,
        SageKingFilterCompareOperator.NotEquals,
        SageKingFilterCompareOperator.Contains,
        SageKingFilterCompareOperator.NotContains,
    };

    public EnumFieldFilterType()
    {
        SupportedCompareOperators = _supportedCompareOperators;
    }

    public override Expression GetFilterExpression(SageKingFilterCompareOperator compareOperator, Expression fieldValueExpr, Expression filterValueExpr)
    {
        Expression ContainsExpression()
        {
            filterValueExpr = Expression.Convert(filterValueExpr, typeof(Enum));
            return Expression.Call(fieldValueExpr, _enumHasFlag, filterValueExpr);
        }

        Expression GetCompareExpr()
        {
            return compareOperator switch
            {
                SageKingFilterCompareOperator.Contains => ContainsExpression(),
                SageKingFilterCompareOperator.NotContains => Expression.Not(ContainsExpression()),
                _ => base.GetFilterExpression(compareOperator, fieldValueExpr, filterValueExpr)
            };
        }

        if (fieldValueExpr.Type.IsTypeNullable())
        {
            Expression notNull = Expression.NotEqual(fieldValueExpr, Expression.Constant(null));
            fieldValueExpr = Expression.MakeMemberAccess(fieldValueExpr, fieldValueExpr.Type.GetMember(nameof(Nullable<int>.Value))[0]);
            return Expression.AndAlso(notNull, GetCompareExpr());
        }

        return GetCompareExpr();
    }
}
